package com.oot2d.entity;

import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Vector2f;
import com.oot2d.util.WorldManager;
import com.oot2d.world.Tile;

public class AABB extends Polygon {
    private float w, h;
    private Vector2f offset;
    private Entity entity;

    //Debug
    public Tile[] col;

    public AABB(float bw, float bh, float xoff, float yoff, Entity e)
    {
        super(new float[]{
                0.0f, 0.0f,
                bw * 2, 0.0f,
                bw * 2, bh * 2,
                0.0f, bh * 2
        });

        entity = e;

        setX(0.0f);
        setY(0.0f);
        w = bw;
        h = bh;
        offset = new Vector2f(xoff, yoff);
    }

    public AABB(AABB a, Entity e)
    {
        super(a.getPoints());
        w = a.w;
        h = a.h;
        x = a.x;
        y = a.y;
        offset = new Vector2f(a.offset);
        entity = e;
    }

    public void setPos(float px, float py)
    {
        setX(px - w + offset.x);
        setY(py - h + offset.y);
    }

    public float getX()
    {
        return x;
    }

    public float getY()
    {
        return y;
    }

    public float getWidth()
    {
        return w;
    }

    public float getHeight()
    {
        return h;
    }

    public float left()
    {
        return x;
    }

    public float right()
    {
        return x + (2 * w);
    }

    public float top()
    {
        return y;
    }

    public float bottom()
    {
        return y + (2 * h);
    }

    private boolean collision(Direction dir)
    {
        //Maximum number of tiles we need to check for collision
        int max;
        Tile[] t;
        boolean c = false;

        //X axis
        if (dir == Direction.Left || dir == Direction.Right) {
            max = (int)(Math.ceil(((w * 2) / 16.0f) + 1));
            t = new Tile[max];

            //Shift AABB to projected position
            setX(x + ((dir == Direction.Left) ? -1 : 1));

            //Left
            if (dir == Direction.Left) {
                for (int i = 0; i < max; i++) {
                    t[i] = WorldManager.currentWorld.tileAt(x, y + (16.0f * i), 0, entity.z);
                }
            }

            //Right
            else {
                for (int i = 0; i < max; i++) {
                    t[i] = WorldManager.currentWorld.tileAt(right(), y + (16.0f * i), 0, entity.z);
                }
            }

            //Check collision in X direction
            Polygon p;
            for (int i = 0; i < max; i++) {
                if ((p = t[i].getBounds()) != null) {
                    if (this.intersects(p))
                        c = true;
                }
            }

        //Y axis
        } else {
            max = (int)(Math.ceil(((h * 2) / 16.0f) + 1));
            t = new Tile[max];

            //Shift AABB to projected position
            setY(y + ((dir == Direction.Up) ? -1 : 1));

            //Up
            if (dir == Direction.Up) {
                for (int i = 0; i < max; i++) {
                    t[i] = WorldManager.currentWorld.tileAt(x + (16.0f * i), y, 0, entity.z);
                }
            }

            //Down
            else {
                for (int i = 0; i < max; i++) {
                    t[i] = WorldManager.currentWorld.tileAt(x + (16.0f * i), bottom(), 0, entity.z);
                }
            }

            //Check collision in Y direction
            Polygon p;
            for (int i = 0; i < max; i++) {
                if ((p = t[i].getBounds()) != null) {
                    if (this.intersects(p))
                        c = true;
                }
            }
        }

        col = t;

        //Move AABB back to starting position
        setPos(entity.x, entity.y);

        return c;
    }

    //Move AABB, handle collision detection
    public void update(int d, boolean corners)
    {
        setPos(entity.x, entity.y);
        float vx = entity.vel.x;
        float vy = entity.vel.y;

        //X axis
        if (vx != 0) {
            boolean c = (vx < 0) ? collision(Direction.Left) : collision(Direction.Right);

            //If there is a collision, stop moving
            if (c) {
                entity.vel.x = 0.0f;

                if (corners) {
                    if(vy == 0){
                        //Check for corners above
                        for (int i = 1; i < 9; i++) {
                            setPos(entity.x, entity.y - i);
                            c = (vx < 0) ? collision(Direction.Left) : collision(Direction.Right);

                            //If there is open space above, move towards it
                            if (!c) {
                                entity.vel.y = -0.1f;
                                break;
                            }
                        }

                        //Check for corners below
                        if (entity.vel.y == 0) {
                            for (int i = 1; i < 9; i++) {
                                setPos(entity.x, entity.y + i);
                                c = (vx < 0) ? collision(Direction.Left) : collision(Direction.Right);

                                //If there is open space below, move towards it
                                if (!c) {
                                    entity.vel.y = 0.1f;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        //Y axis
        if (vy != 0) {
            boolean c = (vy < 0) ? collision(Direction.Up) : collision(Direction.Down);

            //If there is a collision, stop moving
            if (c) {
                entity.vel.y = 0.0f;

                if (corners) {
                    if(vx == 0) {
                        //Check for corners to the left
                        for (int i = 1; i < 9; i++) {
                            setPos(entity.x - i, entity.y);
                            c = (vy < 0) ? collision(Direction.Up) : collision(Direction.Down);

                            //If there is open space above, move towards it
                            if (!c) {
                                entity.vel.x = -0.1f;
                                break;
                            }
                        }

                        //Check for corners to the right
                        if (entity.vel.x == 0) {
                            for (int i = 1; i < 9; i++) {
                                setPos(entity.x + i, entity.y);
                                c = (vy < 0) ? collision(Direction.Up) : collision(Direction.Down);

                                //If there is open space below, move towards it
                                if (!c) {
                                    entity.vel.x = 0.1f;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}